package top.cardone.net.util;

import com.jcraft.jsch.*;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import top.cardone.core.util.action.Action1;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * Created by yao hai tao on 2015/6/11.
 */
@Log4j2
public class FtpUtils {
    public static void ftpAction(String hostname, int port, String username, String password, Action1<FTPClient> action) throws IOException {
        FTPClient ftpClient = null;

        try {
            ftpClient = getFTPClient(hostname, port, username, password);

            action.action(ftpClient);
        } finally {
            close(ftpClient);
        }
    }


    public static void close(FTPClient ftpClient) throws IOException {
        if (ftpClient == null) {
            return;
        }

        if (!ftpClient.isConnected()) {
            return;
        }

        ftpClient.logout();
        //解除连接
        ftpClient.disconnect();
    }

    public static FTPClient getFTPClient(String hostname, int port, String username, String password) throws IOException {
        FTPClient ftpClient = new FTPClient();

        //设置服务器名和端口
        ftpClient.connect(hostname, port);

        int reply = ftpClient.getReplyCode();

        //连接错误的时候报错。
        if (!FTPReply.isPositiveCompletion(reply)) {
            throw new IOException("Can't Connect to :" + hostname);
        }

        //登录
        if (ftpClient.login(username, password) == false) {
            throw new IOException("Invalid user/password");
        }

        // 开启服务器对UTF-8的支持，如果服务器支持就用UTF-8编码，否则就使用本地编码（GBK）.
        if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
            ftpClient.setControlEncoding("UTF-8");
        }

        ftpClient.enterLocalPassiveMode();

        //设置传送文件模式
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

        return ftpClient;
    }

    public static ChannelSftp getChannelSftp(String hostname, int port, String user, String password) throws JSchException {
        Properties properties = new Properties();

        properties.put("StrictHostKeyChecking", "no");

        return getChannelSftp(hostname, port, user, password, properties);
    }

    public static ChannelSftp getChannelSftp(String hostname, int port, String user, String password, Properties properties) throws JSchException {
        Session session = new JSch().getSession(user, hostname, port);

        session.setPassword(password);

        session.setConfig(properties);

        session.connect(30000);

        Channel channel = session.openChannel("sftp");

        return (ChannelSftp) channel;
    }

    public static void sftpAction(String hostname, int port, String user, String password, Action1<ChannelSftp> action) throws JSchException {
        Properties properties = new Properties();

        properties.put("StrictHostKeyChecking", "no");

        sftpAction(hostname, port, user, password, properties, action);
    }

    public static void sftpAction(String hostname, int port, String user, String password, Properties properties, Action1<ChannelSftp> action) throws JSchException {
        ChannelSftp channelSftp = null;

        try {
            channelSftp = getChannelSftp(hostname, port, user, password, properties);

            action.action(channelSftp);
        } finally {
            close(channelSftp);
        }
    }

    public static void close(ChannelSftp channelSftp) {
        if (channelSftp == null) {
            return;
        }

        if (!channelSftp.isConnected()) {
            return;
        }

        //解除连接
        channelSftp.disconnect();
    }

    public static void mkdir(FTPClient ftpClient, String path) throws IOException {
        String[] paths = StringUtils.split(path, "/");

        if (paths.length < 1) {
            return;
        }

        ftpClient.changeWorkingDirectory(ftpClient.printWorkingDirectory());

        String mkdirPath = "/";

        for (String itemPath : paths) {
            if (StringUtils.isNotBlank(itemPath)) {
                itemPath += "/";
            }

            mkdirPath += itemPath;

            if (exists(ftpClient, mkdirPath)) {
                continue;
            }

            ftpClient.mkd(mkdirPath);
        }
    }

    public static boolean exists(FTPClient ftpClient, String path) throws IOException {
        return ftpClient.listFiles(path).length > 0;
    }

    public static void upload(FTPClient ftpClient, String local_file_path, String ftp_file_path) throws IOException {
        final File localFile = new File(local_file_path);

        if (!localFile.exists()) {
            throw new RuntimeException("本地文件不存在：" + local_file_path);
        }

        try (FileInputStream fis = FileUtils.openInputStream(localFile)) {
            upload(ftpClient, ftp_file_path, fis);
        }
    }

    public static void upload(FTPClient ftpClient, String remote, InputStream local) throws IOException {
        String pathname = FilenameUtils.getFullPathNoEndSeparator(remote);

        FtpUtils.mkdir(ftpClient, pathname);

        String from = remote + ".tmp";

        ftpClient.storeFile(from, local);

        ftpClient.rename(from, remote);
    }

    public static void mkdir(ChannelSftp sftp, String path) throws SftpException {
        String[] paths = StringUtils.split(path, "/");

        String mkdirPath = "/";

        for (String itemPath : paths) {
            if (StringUtils.isBlank(itemPath)) {
                continue;
            }

            mkdirPath += itemPath + "/";

            if (exists(sftp, mkdirPath)) {
                continue;
            }

            sftp.mkdir(mkdirPath);
        }
    }

    public static boolean exists(ChannelSftp channelSftp, String path) {
        try {
            return channelSftp.ls(path).size() > 0;
        } catch (SftpException e) {
            log.error(e);
        }

        return false;
    }

    public static void upload(ChannelSftp channelSftp, String local_file_path, String ftp_file_path) throws IOException, SftpException {
        final File localFile = new File(local_file_path);

        if (!localFile.exists()) {
            throw new RuntimeException("本地文件不存在：" + local_file_path);
        }

        if (StringUtils.isBlank(FilenameUtils.getName(ftp_file_path))) {
            throw new RuntimeException("目标文件路径格式不对：" + ftp_file_path);
        }

        try (FileInputStream fis = FileUtils.openInputStream(localFile)) {
            upload(channelSftp, fis, ftp_file_path);
        }
    }

    public static void upload(ChannelSftp channelSftp, InputStream src, String dst) throws SftpException {
        String pathname = FilenameUtils.getFullPathNoEndSeparator(dst);

        FtpUtils.mkdir(channelSftp, pathname);

        channelSftp.cd(pathname);

        String newpath = FilenameUtils.getName(dst);

        String oldpath = newpath + ".tmp";

        channelSftp.put(src, oldpath);

        channelSftp.rename(oldpath, newpath);
    }
}
